﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Seta;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Media.Animation;

namespace Árvore_Binária_de_Pesquisa
{
    public enum ArrowCoordinate
    {
        X1,
        X2,
        Y1,
        Y2
    }

    public class Arrows : CommomFunctions.Arrows
    {
        /// <summary>
        /// Contains the coordinates of an arrow
        /// </summary>
        public struct XYarrows
        {
            public double X1;
            public double X2;
            public double Y1;
            public double Y2;
        }

        /// <summary>
        /// numArrows -> maximum number of arrows in the tree
        /// arrowsId -> represents through its key the nodes to which the arrow is conected too
        /// coloredArrows -> the colored lines that are draw over the original arrow during animations
        /// arrowsToAnimate -> the arrows that will be animated
        /// positionsXYarrows -> keeps the position of the arrows original coordinates
        /// </summary>
        private static int numArrows = 14;
        private static Hashtable arrowsId = new Hashtable();        
        private ArrayList coloredArrows = new ArrayList();
        private ArrayList arrowsToAnimate = new ArrayList();
        private ArrayList positionsXYarrows = new ArrayList();

        public Arrows()
        {
            
        }

        /// <summary>
        /// Creates the arrows that represent the edges of the tree. 
        /// All the arrows are created at once. 
        /// Their visibility is what represents if it is conecting a node or not
        /// </summary>
        /// <param name="Gridi"></param>
        public void createArrows(Grid Gridi)
        {
            for (int i = 0; i < numArrows; i++)
            {
                ArrowLine tempArrow = new ArrowLine();
                Gridi.Children.Add(tempArrow);
                arrows.Add(tempArrow);

                ArrowLine tempColoredArrow = new ArrowLine();
                Gridi.Children.Add(tempColoredArrow);
                coloredArrows.Add(tempColoredArrow);
            }

            foreach (ArrowLine r in arrows)
            {
                r.Visibility = Visibility.Hidden;
                r.Stroke = Brushes.ForestGreen;
                r.StrokeThickness = 2.5;
                r.VerticalAlignment = VerticalAlignment.Stretch;
                r.HorizontalAlignment = HorizontalAlignment.Stretch;
                r.Fill = Brushes.ForestGreen;

                Grid.SetRow(r, 1);
                Grid.SetRowSpan(r, 6);
                Grid.SetColumnSpan(r, 9);
            }

            foreach (ArrowLine r in coloredArrows)
            {

                r.Stroke = Brushes.Orange;
                r.StrokeThickness = 3.5;
                r.VerticalAlignment = VerticalAlignment.Stretch;
                r.HorizontalAlignment = HorizontalAlignment.Stretch;
                r.Fill = Brushes.Orange;
                r.Opacity = 0;

                Grid.SetRow(r, 1);
                Grid.SetRowSpan(r, 6);
                Grid.SetColumnSpan(r, 9);
            }
        }

        /// <summary>
        /// Set the coordinates of the arrow indicated by "ind" and saves its original position
        /// </summary>
        /// <param name="ind"></param>
        /// <param name="x1"></param>
        /// <param name="x2"></param>
        /// <param name="y1"></param>
        /// <param name="y2"></param>
        void setArrowCoordinates(int ind, double x1, double x2, double y1, double y2)
        {
            ((ArrowLine)arrows[ind]).X1 = x1;
            ((ArrowLine)arrows[ind]).X2 = x2;
            ((ArrowLine)arrows[ind]).Y1 = y1;
            ((ArrowLine)arrows[ind]).Y2 = y2;

            ((ArrowLine)coloredArrows[ind]).X1 = x1;
            ((ArrowLine)coloredArrows[ind]).X2 = x2;
            ((ArrowLine)coloredArrows[ind]).Y1 = y1;
            ((ArrowLine)coloredArrows[ind]).Y2 = y2;

            XYarrows xy = new XYarrows();

            xy.X1 = x1;
            xy.X2 = x2;
            xy.Y1 = y1;
            xy.Y2 = y2;

            positionsXYarrows.Insert(ind, xy);

        }

        /// <summary>
        /// Call the function that calculates the arrows margins accolording to the nodes it is connecting
        /// </summary>
        /// <param name="myNodes"></param>
        public void calculateArrowsMargins(Labels myNodes)
        {
            setArrowMargins(0, 0, 1, myNodes);
            setArrowMargins(1, 0, 2, myNodes);
            setArrowMargins(2, 1, 3, myNodes);
            setArrowMargins(3, 1, 4, myNodes);
            setArrowMargins(4, 2, 5, myNodes);
            setArrowMargins(5, 2, 6, myNodes);
            setArrowMargins(6, 3, 7, myNodes);
            setArrowMargins(7, 3, 8, myNodes);
            setArrowMargins(8, 4, 9, myNodes);
            setArrowMargins(9, 4, 10, myNodes);
            setArrowMargins(10, 5, 11, myNodes);
            setArrowMargins(11, 5, 12, myNodes);
            setArrowMargins(12, 6, 13, myNodes);
            setArrowMargins(13, 6, 14, myNodes);
        }
        
        /// <summary>
        /// Calculates the arrow margins and coordinates accolording to the node it is connected too.
        /// The nodes it is connected to also determines the id of the arrow that is saved as
        /// the key of "arrowsId"
        /// </summary>
        /// <param name="ind">The index of the arrow</param>
        /// <param name="p1">The first node to which the arrow is connected to</param>
        /// <param name="p2">The second node to which the arrow is connected to</param>
        /// <param name="myNodes">The nodes of the tree</param>
        private void setArrowMargins(int ind, int p1, int p2, Labels myNodes)
        {
            double sizePt = 994 - myNodes.getLabel(p1).Margin.Right - myNodes.getLabel(p1).Margin.Left;
            double sizePt1 = 994 - myNodes.getLabel(p2).Margin.Right - myNodes.getLabel(p2).Margin.Left;

            setArrowCoordinates(ind, myNodes.getLabel(p1).Margin.Left + (sizePt / 2.0), myNodes.getLabel(p2).Margin.Left + (sizePt1 / 2.0),
            450 - myNodes.getLabel(p1).Margin.Bottom, myNodes.getLabel(p2).Margin.Top);

            arrowsId.Add(p1.ToString() + p2.ToString(), ind);
        }
               
        /// <summary>
        /// Returns the original coordinate "ac" of the arrow indicated by "p"
        /// </summary>
        /// <param name="ac"></param>
        /// <param name="p"></param>
        /// <returns></returns>
        internal double getArrowPosition(ArrowCoordinate ac, int p)
        {
            switch(ac)
            {
                case ArrowCoordinate.X1: return ((XYarrows)positionsXYarrows[p]).X1;
                case ArrowCoordinate.X2: return ((XYarrows)positionsXYarrows[p]).X2;
                case ArrowCoordinate.Y1: return ((XYarrows)positionsXYarrows[p]).Y1;
                case ArrowCoordinate.Y2: return ((XYarrows)positionsXYarrows[p]).Y2;
            }
            return 0;
        }

        /// <summary>
        /// Returns the arrow indicated by "ind"
        /// </summary>
        /// <param name="ind"></param>
        /// <returns></returns>
        internal ArrowLine getArrow(int ind)
        {
            return ((ArrowLine)arrows[ind]);
        }

        /// <summary>
        /// Returns the colored arrow indicated by "ind"
        /// </summary>
        /// <param name="ind"></param>
        /// <returns></returns>
        internal ArrowLine getColoredArrow(int ind)
        {
            return ((ArrowLine)coloredArrows[ind]);
        }

        /// <summary>
        /// Returns the Hashtable that contains the arrows ids
        /// </summary>
        /// <param name="ind"></param>
        /// <returns></returns>
        internal Hashtable getArrows()
        {
            return ((Hashtable)arrowsId);
        }

        /// <summary>
        /// Returns the ArrayList that contains the arrows to animate
        /// </summary>
        /// <param name="ind"></param>
        /// <returns></returns>
        internal ArrayList getArrowsToAnimate()
        {
            return ((ArrayList)arrowsToAnimate);
        }

        /// <summary>
        /// Clears the ArrayList that contains the arrows to animate
        /// </summary>
        /// <param name="ind"></param>
        /// <returns></returns>
        internal void clearArrowsToAnimate()
        {
            arrowsToAnimate.Clear();        
        }

        /// <summary>
        /// Adds the index (indicated by "ind") of the arrow to animate
        /// </summary>
        /// <param name="ind"></param>
        /// <returns></returns>
        internal void addArrowToAnimate(int ind)
        {
            arrowsToAnimate.Add(ind);
        }
    }
}
